#ifndef _MY_LOG_DEFINED_
#define _MY_LOG_DEFINED_
/**
 * avail:
 *      MY_LOG
 *      MY_LOGW
 *      MY_LOGE
 *      MY_LOGV
 *      MY_LOGIF
 *      MY_LOGEIF
 *      MY_FILE_W
 *      MY_INTR
 *      MY_ASSERT
 *      uint64_t __current_ms();
 *      __is_proc_self()
 *      MY_VAR_TOKEN(token)
 *      MY_TRACE("classname")
 *      MY_DURATION_BEGIN, MY_DURATION_END
 */
/**
 * useful macro:
 */
//#define MY_LOG_TAG
//#define MY_LOG_PROCS {"proc_name1", ...};
//#define MY_LOG_NO_V           //no logv
//#define MY_LOG_NO_LOGCAT      //force logcat to stdout

#ifndef MY_LOG_TAG
# define MY_LOG_TAG __FILE__
#endif
#ifndef MY_LOG_PROCS
#define MY_LOG_PROCS {}
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>        //gettid()
#include <time.h>
#include <assert.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>

#define ____MY_VAR_TOKEN(x, y) x##y
#define __MY_VAR_TOKEN(x, y) ____MY_VAR_TOKEN(x, y)
#define MY_VAR_TOKEN(token) __MY_VAR_TOKEN(__my_var_##token##_, __LINE__)

#ifdef __ANDROID__
# include <android/log.h>   //for android build and ndk <system/core/include/>
#  ifdef __cplusplus
#    include <utils/Trace.h>
#    define MY_TRACE(_class)    \
        char MY_VAR_TOKEN(buf)[128]; \
        snprintf(MY_VAR_TOKEN(buf), sizeof(MY_VAR_TOKEN(buf)), "%s::%s", _class, __FUNCTION__); \
        android::ScopedTrace MY_VAR_TOKEN(tracer)(ATRACE_TAG_GRAPHICS, MY_VAR_TOKEN(buf))
#  endif
# ifndef MY_LOG_NO_LOGCAT
#  define ____LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "MY_LOG", ##__VA_ARGS__)
#  define _MY_ENDL_
# endif
#else
# include <sys/syscall.h>   //gettid
# include <sys/time.h>      //gettimeofday
# define gettid() syscall(__NR_gettid)
#endif
#ifndef ____LOGD
# define ____LOGD printf
#endif
#ifndef _MY_ENDL_
# define _MY_ENDL_ "\n"
#endif
static char __my_proc_name[128];
static __attribute__ ((unused)) int __is_proc_self() {
    FILE *f_status = NULL;
    int ret = 0;
    static const char * const __MY_LOG_PROCS[] = MY_LOG_PROCS;

    memset(__my_proc_name, 0, sizeof(__my_proc_name));
    f_status = fopen("/proc/self/cmdline", "r");
    fread(__my_proc_name, sizeof(__my_proc_name), 1, f_status);
    fclose(f_status);

    if (sizeof(__MY_LOG_PROCS) == 0) {
        ret = 1;
    } else {
        int loop = (int) (sizeof(__MY_LOG_PROCS) / sizeof(__MY_LOG_PROCS[0]) - 1);

        for(; loop >= 0; loop --) {
            //____LOGD("readed: [%s], __MY_LOG_PROCS[%i]: [%s]", __my_proc_name, loop, __MY_LOG_PROCS[loop]);
            if ( strstr(__MY_LOG_PROCS[loop], __my_proc_name) || strstr(__my_proc_name, __MY_LOG_PROCS[loop]) ) {
                ret = 1;
                break;
            }
        }
    }
    return ret;
}

static __attribute__ ((unused)) const char *__get_prefix(char *buf, int bufsize) {
#ifdef __ANDROID__
# ifndef MY_LOG_NO_LOGCAT
    (void) buf;
    (void) bufsize;
    return "\0";
# endif
#endif
    if (NULL == buf || bufsize == 0) {
        return "\0";
    }
    struct timeval _tv;
    struct tm _tm;
    gettimeofday(&_tv, NULL);
    localtime_r(&_tv.tv_sec, &_tm);
    memset(buf, '\0', bufsize);
    snprintf(buf, bufsize, "%02d-%02d %02d:%02d:%02d.%03d ",
            1+_tm.tm_mon, _tm.tm_mday, _tm.tm_hour, _tm.tm_min, _tm.tm_sec, (int)_tv.tv_usec/1000);
    return buf;
}

#define __my_log(color, _func_, _line_, ...) do { \
    if (__is_proc_self()) { \
        char formated[1024] = {0}; \
        char buf_prefix[64] = {0};\
        snprintf(formated, sizeof(formated), ##__VA_ARGS__); \
        ____LOGD("%s" "\033[" "%s[%s %s T%ld %s L%d]: %s" "\033[0m" _MY_ENDL_, \
                __get_prefix(buf_prefix, sizeof(buf_prefix)), color, __my_proc_name, MY_LOG_TAG, (long) gettid(), _func_, _line_, formated); \
    }\
} while (0) //define MY_LOG
#define MY_LOG(...)  __my_log("01;36m", __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define MY_LOGW(...) __my_log("01;32m", __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define MY_LOGE(...) __my_log("01;31m", __FUNCTION__, __LINE__, ##__VA_ARGS__)
#ifdef  MY_LOG_NO_V
# define MY_LOGV(...)
#else
# define MY_LOGV(...) __my_log("01;37m", __FUNCTION__, __LINE__, ##__VA_ARGS__)
#endif

#define MY_LOGIF(cond, fmt, ...)  do {if(cond){MY_LOG(fmt, ##__VA_ARGS__); } } while(0)
#define MY_LOGEIF(cond, fmt, ...) do {if(cond){MY_LOGE(fmt, ##__VA_ARGS__);} } while(0)
#define MY_ASSERT(cond) do{/*avoid run twice*/ \
    const void * MY_VAR_TOKEN(_cond_) = (const void *) (cond); \
    MY_LOGEIF((!MY_VAR_TOKEN(_cond_)), "'" #cond "' Assert Fail!"); \
    if (!MY_VAR_TOKEN(_cond_)) {\
        exit(-1); \
    }\
    /*assert(MY_VAR_TOKEN(_cond_)); \*/ \
} while(0)


static __attribute__ ((unused)) uint64_t __current_ms() {
    struct timeval _tv;
    gettimeofday(&_tv, NULL);
    return ((_tv.tv_sec*1000) + (_tv.tv_usec/1000));
}

static uint64_t __MY_TIME_START = 0;
#define MY_DURATION_BEGIN do{                                   \
    __MY_TIME_START = __current_ms();                           \
    __my_log("01;35m", __FUNCTION__, __LINE__,"Duration Start");\
} while(0)

#define MY_DURATION_END   do{                                   \
    __my_log("01;35m", __FUNCTION__, __LINE__,                  \
            "Duration End: %lu ms",                             \
            (unsigned long)(__current_ms()-__MY_TIME_START));   \
} while(0)

static int __attribute__ ((unused)) __my_intr(char *path, const char *_func_, int _line_) {
    char strvalue[32] = {'\0'};
    FILE *filePath = fopen(path, "r");
    int ret = -1024;
    if (filePath != NULL) {
        if(fread(strvalue, 1, sizeof(strvalue), filePath) > 0) {
            ret = atoi(strvalue);
            __my_log("01;36m", _func_, _line_, "%s readed %d ", path, ret);
        } else {
            __my_log("01;36m", _func_, _line_, "Fail to read: %s", path);
        }
        fclose(filePath);
    } else {
        __my_log("01;36m", _func_, _line_, "Fail to open: %s", path);
    }
    return ret;
}

#define MY_FILE_W(path, buf, len) do {\
    int fd = open(path, O_WRONLY|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO); \
    if (fd != -1) { \
        int ret = write(fd, buf, len); \
        if(ret != (int)len) { \
            MY_LOGE("ERROR write to %s (%d, %d): %s", path, ret, len, strerror(errno));\
        } else {\
            MY_LOG("written %d bytes to %s", len, path);\
        }\
        close(fd); \
    } else { \
        MY_LOGE("Fail to open: %s, err:%s", path, strerror(errno));\
    }\
}while (0)

#define MY_INTR(path)  __my_intr(path, __FUNCTION__, __LINE__)

#endif //_MY_LOG_DEFINED_
